#include "model_data_reader.h"
#include "md_ver_1_0.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>



#define BUFF_SIZE 1024
#define REALLOC_RESIZE	16


ModelData * ReadObjFile(FILE *input)
{
	//coded according to md_ver_1.0
	//need to extend to other formats...

	char buff[BUFF_SIZE] = "";


	int vertexBuffSize = REALLOC_RESIZE;
	int normalBuffSize = REALLOC_RESIZE;
	int textureBuffSize = REALLOC_RESIZE;

	int totalVertex = 0;
	int totalNormal = 0;
	int totalTexture = 0;

	float *vertexBuff = (float *)malloc(sizeof(float)*vertexBuffSize * 3);
	float *normalBuff = (float *)malloc(sizeof(float)*normalBuffSize * 3);
	float *textureBuff = (float *)malloc(sizeof(float)*textureBuffSize * 3);

	void *data = 0;
	int dataCount = 0;
	int format = 0;
	int meshCount = 0;
	void *meshes = 0;
	int meshOffset = 0;
	int primitive = 0;

	printf("\nreading input obj file...");

	while(!feof(input))
	{
		fscanf(input,"%s",buff);
		if(!strcmp(buff,"#"))		//reading comment
		{
			fgets(buff,BUFF_SIZE,input);
			//check if it reached end
			if(feof(input))
			{
				printf("\nERROR::FILE ENDED IN BETWEEN");
				break;
			}
			if(buff[strlen(buff)-1] != '\n')
			{
				printf("\nERROR::FAILED DUE TO BUFF SIZE EXCEEDS");
				break;
			}
			else
			{
				printf("%s",buff);
			}
		}
		else if(!strcmp(buff,"v"))
		{
			totalVertex ++;
			if(totalVertex >= vertexBuffSize)
			{
				vertexBuffSize += REALLOC_RESIZE;
				vertexBuff = (float *)realloc(vertexBuff,sizeof(float)*vertexBuffSize * 3);
			}
			int index = (totalVertex - 1) * 3;
			fscanf(input,"%f %f %f",&vertexBuff[index + 0],&vertexBuff[index + 1],&vertexBuff[index + 2]);
			printf("\nv %f %f %f",vertexBuff[index + 0],vertexBuff[index + 1],vertexBuff[index + 2]); 

		}
		else if(!strcmp(buff,"vn"))
		{
			totalNormal ++;
			if(totalNormal >= normalBuffSize)
			{
				normalBuffSize += REALLOC_RESIZE;
				normalBuff = (float *)realloc(normalBuff,sizeof(float)*normalBuffSize * 3);
			}
			int index = (totalNormal - 1) * 3;
			fscanf(input,"%f %f %f",&normalBuff[index + 0],&normalBuff[index + 1],&normalBuff[index + 2]);
			printf("\nvn %f %f %f",normalBuff[index + 0],normalBuff[index + 1],normalBuff[index + 2]); 

		}
		else if(!strcmp(buff,"vt"))
		{
			totalTexture ++;
			if(totalTexture >= textureBuffSize)
			{
				textureBuffSize += REALLOC_RESIZE;
				textureBuff = (float *)realloc(textureBuff,sizeof(float)*textureBuffSize * 3);
			}
			int index = (totalTexture - 1) * 3;
			fscanf(input,"%f %f %f",&textureBuff[index + 0],&textureBuff[index + 1],&textureBuff[index + 2]);
			printf("\nvn %f %f %f",textureBuff[index + 0],textureBuff[index + 1],textureBuff[index + 2]); 

		}
		else if(!strcmp(buff,"g"))
		{
			fgets(buff,BUFF_SIZE,input);

			//coded according to md_ver_1.0
			//need to extend to other formats...
			//MDv1_0_Mesh **meshes;
			meshes = (MDv1_0_Mesh *)realloc(meshes,(meshCount + 1) * sizeof(MDv1_0_Mesh));	
			meshCount ++;
			((MDv1_0_Mesh *)meshes)[meshCount - 1].dataOffset = meshOffset;
			//((MDv1_0_Mesh *)meshes)[meshCount - 1]->materialName
			int len =strlen(buff);
			if(len >= 0x10)
				buff[0x0f] = '\0';
			else
				buff[len - 2] = '\0';
			strcpy(((MDv1_0_Mesh *)meshes)[meshCount - 1].name,buff);	
			((MDv1_0_Mesh *)meshes)[meshCount - 1].size = 0;
		}
		else if(!strcmp(buff,"usemtl"))
		{
			fgets(buff,BUFF_SIZE,input);
			int len =strlen(buff);
			if(len >= 0x40)
				buff[0x0f] = '\0';
			else
				buff[len - 2] = '\0';
			//buff+1 to remove space in name
			strcpy(((MDv1_0_Mesh *)meshes)[meshCount - 1].materialName,buff + 1);	

		}
		else if(!strcmp(buff,"f"))
		{
			char *pStr;
			int vertexIndex = 0;
			int normalIndex = 0;
			int textureIndex = 0;
			fgets(buff,BUFF_SIZE,input);
			pStr = strtok (buff," ,.-");
			int priC = 0;
			while(pStr != 0)
			{
				priC ++;
				//coded according to md_ver_1.0
				//need to extend to other formats...
				if (sscanf(pStr, "%i", &vertexIndex,&normalIndex) == 1)
				{
					vertexIndex -= 1; //indexing from zero so subtracting -1
					normalIndex -= 1;
					format = MDF_V3F;
				}
				else if (sscanf(pStr, "%i//%i", &vertexIndex,&normalIndex) == 2)
				{
					vertexIndex -= 1; //indexing from zero so subtracting -1
					normalIndex -= 1;
					format = MDF_N3F_V3F;
				}
				else if(sscanf(pStr, "%i/%i/%i", &vertexIndex,&textureIndex,&normalIndex ) == 3)
				{
					vertexIndex -= 1; //indexing from zero so subtracting -1
					normalIndex -= 1;
					textureIndex -= 1;
					format = MDF_T2F_N3F_V3F;
				}
					
				//depending on format it will decide which format struct
				switch(format)
				{
				case MDF_T2F_N3F_V3F:
					{
						data = (MDF_S_T2F_N3F_V3F *)realloc(data,(dataCount + 1) * sizeof(MDF_S_T2F_N3F_V3F));				
						
						MDF_S_T2F_N3F_V3F *d = &((MDF_S_T2F_N3F_V3F *)data)[dataCount];
						memset(d,0,sizeof(MDF_S_T2F_N3F_V3F));

						d->vx = vertexBuff[3 * vertexIndex + 0];
						d->vy = vertexBuff[3 * vertexIndex + 1];
						d->vz = vertexBuff[3 * vertexIndex + 2];
						d->nx = normalBuff[3 * normalIndex + 0];
						d->ny = normalBuff[3 * normalIndex + 1];
						d->nz = normalBuff[3 * normalIndex + 2];
						d->u = textureBuff[3 * textureIndex + 0];
						d->v = textureBuff[3 * textureIndex + 1];

						printf("\nv=(%f,%f,%f) n=(%f,%f,%f) t=(%f,%f)", d->vx, d->vy, d->vz, d->nx, d->ny, d->nz, d->u, d->v);

					}
					break;
				case MDF_N3F_V3F:
					{
						data = (MDF_S_N3F_V3F *)realloc(data,(dataCount + 1) * sizeof(MDF_S_N3F_V3F));				
						
						MDF_S_N3F_V3F *d = &((MDF_S_N3F_V3F *)data)[dataCount];
						memset(d,0,sizeof(MDF_S_N3F_V3F));

						d->vx = vertexBuff[3 * vertexIndex + 0];
						d->vy = vertexBuff[3 * vertexIndex + 1];
						d->vz = vertexBuff[3 * vertexIndex + 2];
						d->nx = normalBuff[3 * normalIndex + 0];
						d->ny = normalBuff[3 * normalIndex + 1];
						d->nz = normalBuff[3 * normalIndex + 2];
					}
					break;
				case MDF_V3F:
					{
						data = (MDF_S_V3F *)realloc(data,(dataCount + 1) * sizeof(MDF_S_V3F));				
						
						MDF_S_V3F *d = &((MDF_S_V3F *)data)[dataCount];
						memset(d,0,sizeof(MDF_S_V3F));

						d->vx = vertexBuff[3 * vertexIndex + 0];
						d->vy = vertexBuff[3 * vertexIndex + 1];
						d->vz = vertexBuff[3 * vertexIndex + 2];
					}
					break;
				}
				dataCount ++;
				((MDv1_0_Mesh *)meshes)[meshCount - 1].size ++;
				meshOffset ++;
				

				pStr = strtok (NULL, " ,.-\n\r");
			}

			if(priC == 3)
				primitive = MDF_TRIANGLES;
			else if(priC == 4)
				primitive = MDF_QUADS;
			else
				primitive = MDF_POLYGON;
	
		}
		else 
		{

		}
	}

	free(vertexBuff);
	free(normalBuff);
	free(textureBuff);

	ModelData *md = new MDVer1_0(data,dataCount,format,meshes,meshCount,primitive);

	return md;
}


ModelData * ReadMDFile(FILE *fp)
{
	ModelData *md = 0;
	int modelDataVersion = 0;
	fread(&modelDataVersion,sizeof(int),1,fp);
	printf("\n%d",modelDataVersion);
///....
	switch(modelDataVersion)
	{
	case MD_VER_1_0:
		{
			MDVer1_0 *modelData = new MDVer1_0();
			md = modelData;

			//starting static data of MD_VER_1_0
			fseek(fp,0x10,SEEK_SET);
			fread(&modelData->format,sizeof(int),1,fp);
			fread(&modelData->size,sizeof(int),1,fp);
			fread(&modelData->totalMeshes,sizeof(int),1,fp);
			modelData->CalculateStride();

			//starting dynamic data of MD_VER_1_0
			fseek(fp,0x40,SEEK_SET);
			modelData->meshes = (MDv1_0_Mesh *)malloc(modelData->totalMeshes * sizeof(MDv1_0_Mesh));
			fread(modelData->meshes,sizeof(MDv1_0_Mesh),modelData->totalMeshes,fp);
			modelData->data = malloc(modelData->stride * modelData->size);
			fread(modelData->data,modelData->stride,modelData->size,fp);


		}
		break;
	}

	return md;

}